/*
 * @Author: wangwei wwdqq7@qq.com
 * @Date: 2025-04-01 15:00:00
 * @LastEditors: wangwei wwdqq7@qq.com
 * @LastEditTime: 2025-04-01 22:30:58
 * @FilePath: /FullStack/pro/typeorm-mysql/src/auth/guards/permissions.guard.ts
 * @Description: 权限守卫，用于检查用户是否具有访问特定API端点所需的权限
 */
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

import { PERMISSIONS_KEY } from '@/auth/decorators/permissions.decorator';
import { UserService } from '@/user/user.service';

type AuthenticatedRequest = {
  user?: {
    userId: number;
  };
};

interface Role {
  id: number;
  name?: string;
}

interface Permission {
  id: number;
  action: string;
  resource?: string;
}

@Injectable()
export class PermissionsGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private userService: UserService,
  ) {}

  /**
   * 检查用户是否具有访问特定API端点所需的权限
   * @param context 执行上下文
   * @returns 是否允许访问
   */
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
      PERMISSIONS_KEY,
      [context.getHandler(), context.getClass()],
    );

    // 如果没有设置权限要求，则允许访问
    if (!requiredPermissions || requiredPermissions.length === 0) {
      return true;
    }

    const request = context.switchToHttp().getRequest<AuthenticatedRequest>();
    const { user } = request;
    if (!user?.userId) {
      return false;
    }

    // 获取用户的权限
    const userRoles = await this.userService.getUserRoles(user.userId);
    let hasPermission = false;

    // 检查用户是否具有所需权限
    for (const role of userRoles as Role[]) {
      const rolePermissions = await this.userService.getRolePermissions(
        role.id,
      );
      const permissionActions = (rolePermissions as Permission[]).map(
        (p) => p.action,
      );

      // 如果用户具有所需的所有权限，则允许访问
      if (requiredPermissions.every((p) => permissionActions.includes(p))) {
        hasPermission = true;
        break;
      }
    }

    return hasPermission;
  }
}
